package tcpechoserver;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Iterator;

/**
 *
 * @author rafal
 */
public class TCPEchoServer {

    public static void main(String[] args) {
        try {

            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.socket().bind(new InetSocketAddress(5555));
            
            ArrayList<SocketChannel> channels = new ArrayList<SocketChannel>();
            //wait for incoming connections
            TCPListenerThread tcpListenerThread=null;
            while (true) {
                
                System.out.println("Waiting for connection.....");
                SocketChannel clientSocketChannel = null;
                clientSocketChannel = serverSocketChannel.accept();
                clientSocketChannel.configureBlocking(false);
                System.out.println("Connection successful");
                channels.add(clientSocketChannel);

                
                //create new selector and register all the current channels
                Selector selector = Selector.open();
                Iterator it = channels.iterator();
                while (it.hasNext()) {
                    SocketChannel sc;
                    sc = (SocketChannel) it.next();
                    sc.register(selector, sc.validOps());
                }
                if(tcpListenerThread!=null){
                    tcpListenerThread.stopWorking();
                }
                //create new fresh thread for new selector
                tcpListenerThread = new TCPListenerThread(selector);
                tcpListenerThread.start();

                
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }









        /*
         // TODO code application logic here
         try {
         ServerSocket serverSocket = null;
         serverSocket = new ServerSocket(5555);
            
         System.out.println("Waiting for connection.....");
         Socket clientSocket = null;
         clientSocket = serverSocket.accept();
            
         System.out.println("Connection successful");
         BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
         String cAddress = "";
         String inputLine;
         cAddress = clientSocket.getInetAddress().toString();

         while ((inputLine = in.readLine()) != null) {
         System.out.println("Server: " + inputLine + "  " + cAddress + " ");
         }

         in.close();
         clientSocket.close();
         serverSocket.close();
            
         } catch (Exception ex) {
         }
         * */
    }
}

class TCPListenerThread extends Thread {

    Selector selector;
    boolean work = true;

    public TCPListenerThread(Selector selector) {
        try {
            this.selector = selector;
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }
    

    @Override
    public void run() {
        try {
            while (work && selector!=null) {
                try {
                    // Wait for an event
                    selector.select(500);
                } catch (IOException e) {
                    // Handle error with selector
                    break;
                }

                // Get list of selection keys with pending events
                Iterator it = selector.selectedKeys().iterator();

                // Process each key at a time
                while (it.hasNext()) {
                    // Get the selection key
                    SelectionKey selKey = (SelectionKey) it.next();

                    // Remove it from the list to indicate that it is being processed
                    it.remove();

                    try {
                        processSelectionKey(selKey);
                    } catch (IOException e) {
                        // Handle error with channel and unregister
                        selKey.cancel();
                    }
                }

            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }

    public void processSelectionKey(SelectionKey selKey) throws IOException {
        // Since the ready operations are cumulative,
        // need to check readiness for each operation
        if (selKey.isValid() && selKey.isConnectable()) {
            // Get channel with connection request
            SocketChannel sChannel = (SocketChannel) selKey.channel();
            boolean success = sChannel.finishConnect();
            if (!success) {
                // An error occurred; handle it
                // Unregister the channel with this selector
                selKey.cancel();
            }
        }
        if (selKey.isValid() && selKey.isReadable()) {
            // Get channel with bytes to read
            SocketChannel clientSocketChannel = (SocketChannel) selKey.channel();

            BufferedReader in = new BufferedReader(new InputStreamReader(clientSocketChannel.socket().getInputStream()));
            String cAddress = "";
            String inputLine;
            cAddress = clientSocketChannel.socket().getInetAddress().toString();
            int cPort = clientSocketChannel.socket().getPort();

            while ((inputLine = in.readLine()) != null) {
                System.out.println("Server: " + inputLine + "  " + cAddress + " : " + cPort);
            }

        }
    }

    void stopWorking() {
        work = false;
    }

    
}